/*
 * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* ScriptData
 SDName: mob_anubisath_sentinel
 SD%Complete: 95
 SDComment: Shadow storm is not properly implemented in core it should only pTarget ppl outside of melee range.
 SDCategory: Temple of Ahn'Qiraj
 EndScriptData */

#include "ScriptPCH.h"
#include "WorldPacket.h"

#include "Item.h"
#include "Player.h"
#include "Spell.h"

#include "Cell.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"

#define SPELL_MENDING_BUFF      2147

#define SPELL_KNOCK_BUFF        21737
#define SPELL_KNOCK             25778
#define SPELL_MANAB_BUFF        812
#define SPELL_MANAB             25779

#define SPELL_REFLECTAF_BUFF    13022
#define SPELL_REFLECTSFr_BUFF   19595
#define SPELL_THORNS_BUFF       25777

#define SPELL_THUNDER_BUFF      2834
#define SPELL_THUNDER           8732

#define SPELL_MSTRIKE_BUFF      9347
#define SPELL_MSTRIKE           24573

#define SPELL_STORM_BUFF        2148
#define SPELL_STORM             26546

class mob_anubisath_sentinel: public CreatureScript {
public:
    mob_anubisath_sentinel() :
            CreatureScript("mob_anubisath_sentinel") {
    }

    CreatureAI* GetAI(Creature* pCreature) const {
        return new aqsentinelAI(pCreature);
    }

    struct aqsentinelAI: public ScriptedAI {
        uint32 ability;
        int abselected;

        void selectAbility(int asel) {
            switch (asel) {
            case 0:
                ability = SPELL_MENDING_BUFF;
                break;
            case 1:
                ability = SPELL_KNOCK_BUFF;
                break;
            case 2:
                ability = SPELL_MANAB_BUFF;
                break;
            case 3:
                ability = SPELL_REFLECTAF_BUFF;
                break;
            case 4:
                ability = SPELL_REFLECTSFr_BUFF;
                break;
            case 5:
                ability = SPELL_THORNS_BUFF;
                break;
            case 6:
                ability = SPELL_THUNDER_BUFF;
                break;
            case 7:
                ability = SPELL_MSTRIKE_BUFF;
                break;
            case 8:
                ability = SPELL_STORM_BUFF;
                break;
            }
        }

        aqsentinelAI(Creature *c) :
                ScriptedAI(c) {
            ClearBuddyList();
            abselected = 0; // just initialization of variable
        }

        uint64 NearbyGUID[3];

        void ClearBuddyList() {
            NearbyGUID[0] = NearbyGUID[1] = NearbyGUID[2] = 0;
        }

        void AddBuddyToList(uint64 CreatureGUID) {
            if (CreatureGUID == me->GetGUID())
                return;

            for (int i = 0; i < 3; ++i) {
                if (NearbyGUID[i] == CreatureGUID)
                    return;
                if (!NearbyGUID[i]) {
                    NearbyGUID[i] = CreatureGUID;
                    return;
                }
            }
        }

        void GiveBuddyMyList(Creature *c) {
            aqsentinelAI *cai = CAST_AI(aqsentinelAI, (c)->AI());
            for (int i = 0; i < 3; ++i)
                if (NearbyGUID[i] && NearbyGUID[i] != c->GetGUID())
                    cai->AddBuddyToList(NearbyGUID[i]);
            cai->AddBuddyToList(me->GetGUID());
        }

        void SendMyListToBuddies() {
            for (int i = 0; i < 3; ++i)
                if (Creature *pNearby = Unit::GetCreature(*me, NearbyGUID[i]))
                    GiveBuddyMyList(pNearby);
        }

        void CallBuddiesToAttack(Unit *who) {
            for (int i = 0; i < 3; ++i) {
                Creature *c = Unit::GetCreature(*me, NearbyGUID[i]);
                if (c) {
                    if (!c->isInCombat()) {
                        c->SetNoCallAssistance(true);
                        if (c->AI())
                            c->AI()->AttackStart(who);
                    }
                }
            }
        }

        void AddSentinelsNear(Unit * /*nears*/) {
            std::list<Creature*> assistList;
            me->GetCreatureListWithEntryInGrid(assistList, 15264, 70.0f);

            if (assistList.empty())
                return;

            for (std::list<Creature*>::const_iterator iter = assistList.begin();
                    iter != assistList.end(); ++iter)
                AddBuddyToList((*iter)->GetGUID());
        }

        int pickAbilityRandom(bool *chosenAbilities) {
            for (int t = 0; t < 2; ++t) {
                for (int i = !t ? (rand() % 9) : 0; i < 9; ++i) {
                    if (!chosenAbilities[i]) {
                        chosenAbilities[i] = true;
                        return i;
                    }
                }
            }
            return 0; // should never happen
        }

        void GetOtherSentinels(Unit *who) {
            bool *chosenAbilities = new bool[9];
            memset(chosenAbilities, 0, 9 * sizeof(bool));
            selectAbility(pickAbilityRandom(chosenAbilities));

            ClearBuddyList();
            AddSentinelsNear(me);
            int bli;
            for (bli = 0; bli < 3; ++bli) {
                if (!NearbyGUID[bli])
                    break;

                Creature *pNearby = Unit::GetCreature(*me, NearbyGUID[bli]);
                if (!pNearby)
                    break;

                AddSentinelsNear(pNearby);
                CAST_AI(aqsentinelAI, pNearby->AI())->gatherOthersWhenAggro =
                        false;
                CAST_AI(aqsentinelAI, pNearby->AI())->selectAbility(
                        pickAbilityRandom(chosenAbilities));
            }
            /*if (bli < 3)
             DoYell("I dont have enough buddies.", LANG_NEUTRAL, 0);*/
            SendMyListToBuddies();
            CallBuddiesToAttack(who);

            delete[] chosenAbilities;
        }

        bool gatherOthersWhenAggro;

        void Reset() {
            if (!me->isDead()) {
                for (int i = 0; i < 3; ++i) {
                    if (!NearbyGUID[i])
                        continue;
                    if (Creature *pNearby = Unit::GetCreature(*me, NearbyGUID[i])) {
                        if (pNearby->isDead())
                            pNearby->Respawn();
                    }
                }
            }
            ClearBuddyList();
            gatherOthersWhenAggro = true;
        }

        void GainSentinelAbility(uint32 id) {
            me->AddAura(id, me);
        }

        void EnterCombat(Unit * who) {
            if (gatherOthersWhenAggro)
                GetOtherSentinels(who);

            GainSentinelAbility(ability);
            DoZoneInCombat();
        }

        void JustDied(Unit* /*who*/) {
            for (int ni = 0; ni < 3; ++ni) {
                Creature *sent = Unit::GetCreature(*me, NearbyGUID[ni]);
                if (!sent)
                    continue;
                if (sent->isDead())
                    continue;
                sent->ModifyHealth(int32(sent->CountPctFromMaxHealth(50)));
                CAST_AI(aqsentinelAI, sent->AI())->GainSentinelAbility(ability);
            }
        }

        Unit *GetHatedManaUser() const {
            std::list<HostileReference*>::const_iterator i;
            for (i = me->getThreatManager().getThreatList().begin();
                    i != me->getThreatManager().getThreatList().end(); ++i) {
                Unit* pUnit = Unit::GetUnit((*me), (*i)->getUnitGuid());
                if (pUnit->getPowerType() == POWER_MANA)
                    return pUnit;
            }
            return NULL;
        }

        Unit* GetAuraEffectTriggerTarget(uint32 spellId,
                uint8 /*effIndex*/) const {
            switch (spellId) {
            case SPELL_KNOCK_BUFF:
            case SPELL_THUNDER_BUFF:
            case SPELL_MSTRIKE_BUFF:
            case SPELL_STORM_BUFF:
                return me->getVictim();

            case SPELL_MANAB_BUFF:
                return GetHatedManaUser();

            case SPELL_MENDING_BUFF:
            case SPELL_REFLECTAF_BUFF:
            case SPELL_REFLECTSFr_BUFF:
            case SPELL_THORNS_BUFF:
            default:
                return me;
            }
        }
    };
};

void AddSC_mob_anubisath_sentinel() {
    new mob_anubisath_sentinel();
}
